<?

class Smutty_Router {

	var $controller,
			$action,
			$id;

	var $defController,
		$defAction,
		$defId;

	/**
	 *  contructor.
	 *
	 */

	function Smutty_Router() {

		// work out default route args
		$cfg = Smutty_Config::getInstance();
		$this->defController = 'Index';
		$this->defAction = 'index';
		$this->defId = false;
		if ( $cfg->route->default ) {
			$defs = explode( '/', $cfg->route->default );
			if ( $defs[0] ) $this->defController = ucfirst($defs[0]);
			if ( $defs[1] ) $this->defAction = $defs[1];
			if ( $defs[2] ) $this->defId = $defs[2];
		}

		// set default values
		$url = explode(  '/', Smutty_Utils::getSmuttyUrl() );
		$this->controller = Smutty_Router::isValidControllerName($url[0])
			? ucfirst($url[0]) : $this->defController;
		$this->action = Smutty_Router::isValidActionName($url[1])
			? strtolower($url[1]) : $this->defAction;
		$this->id = Smutty_Router::isValidId($url[2])
			? $url[2] : $this->defId;

		// check the users routes to see if anything matches
		foreach ( $cfg->route as $controllerName => $routeSpec ) {

			if ( $controllerName == 'default' ) next;
			elseif ( strtolower($controllerName) == strtolower($this->controller) ) {

				// is the route specified by action?
				if ( is_object($routeSpec) ) {
					// by action.  need to fetch first (assume the only)
					// property for the object which should be the
					// name of the action.
					foreach ( $routeSpec as $action => $spec ) {
						// check we're matching the current action
						if ( $action == $this->action ) {
							$this->id = $this->defId;
							$this->processCustomRoute( $spec, array_slice($url,2) );
						}
						break;
					}
				}
	
				// no it's by controller
				else {
					$this->action = $this->defAction;
					$this->id = $this->defId;
					$this->processCustomRoute( $routeSpec, array_slice($url,1) );
				}

			}

		}

	}

	/**
	 *  this function process the route definition and sets
	 *  the values accordingly.  the names "action" and "id"
	 *  are special and change the current values.
	 *
	 *  @param [spec] spec string
	 *  @param [url] array of url parts
	 *
	 */

	function processCustomRoute( $spec, $url ) {

		$parts = explode( '/', $spec );
		$data =& Smutty_Data::getInstance();

		for ( $i=0; $i<sizeof($parts); $i++ )
			switch ( $parts[$i] ) {
				case 'action':
				case 'id':
					// only override if the value is set, otherwise
					// we just want to use the default
					if ( $url[$i] )
						$this->$parts[$i] = $url[$i];
					break;
				default:
					$data->set( $parts[$i], $url[$i] );
			}

	}

	/**
	 *  tests if a given controller name is valid
	 *
	 *  @param [name] the name to test
	 *  @return boolean indicating validity
	 *
	 */

	function isValidControllerName( $name ) {
		// test is the same at the moment
		return Smutty_Router::isValidActionName( $name );
	}

	/**
	 *  tests if a given action name is valid
	 *
	 *  @param [name] the name to test
	 *  @return boolean indicating validity
	 *
	 */

	function isValidActionName( $name ) {
		// remove valid chars, if there's anything left
		// then it's not valid
		$badChars = preg_replace( '/[A-Za-z_0-9]/', '', $name );
		return ( $name && !$badChars );
	}

	/**
	 *  tests if a given id is valid
	 *
	 *  @param [id] the id to test
	 *  @return boolean indicating validity
	 *
	 */

	function isValidId( $id ) {
		// test is the same at the moment
		return Smutty_Router::isValidActionName( $id );
	}

	/**
	 *  returns the name of the default controller
	 *
	 *  @return String
	 *
	 */

	function getDefaultControllerName() {
		return $this->defController;
	}

	/**
	 *  returns the instance of this class
	 *
	 *  @return Smutty_Router
	 *
	 */

	function getInstance() {
		static $instance;
		if ( $instance == null )
			$instance = new Smutty_Router();
		return $instance;
	}

	/**
	 *  returns the name of the current controller
	 *
	 *  @return String
	 *
	 */

	function getController() {
		return $this->controller;
	}

	/**
	 *  returns the current action
	 *
	 *  @return String
	 *
	 */

	function getAction() {
		return $this->action;
	}

	/**
	 *  dispatches the request
	 *
	 */

	function dispatch() {

		// first need to include controller class
		$controllerFile = 'application/controllers/' . $this->controller . 'Controller.php';
		if ( !file_exists($controllerFile) )
			new Smutty_Exception( ERR_CONTROLLER_INVALID );
		require $controllerFile;

		// now instantiate controller
		$controllerClass = $this->controller . 'Controller';
		$controller = new $controllerClass();
		if ( !$controller )
			new Smutty_Exception( ERR_CONTROLLER_INVALID );

		// make sure the method exists
		$action = $this->action . 'Action';
		if ( !method_exists($controller,$action) )
			new Smutty_Exception( ERR_ACTION_INVALID );
		// if we have the reflection classes we can check
		// for visibility modifiers
		if ( class_exists('ReflectionClass') ) {
			$refl = new ReflectionClass( $controllerClass );
			$meth = $refl->getMethod( $action );
			if ( !$meth->isPublic() )
				new Smutty_Exception( ERR_ACTION_INVALID );
		}

		// we're ready to call the action, so get the data
		// that we're gonna pass to it and go go go!
		$controller->$action(
			$this->id,
			Smutty_Data::getInstance(),
			Smutty_Session::getUser()
		);

	}

}

?>